package main

import "fmt"

type Any interface{}

type Car struct {
	Model        string
	Manufacturer string
	BuildYear    int
}

type Cars []*Car

func (c Cars) Process(f func(car *Car)) {
	for _, c := range c {
		f(c)
	}
}

func (c Cars) FindAll(f func(car *Car) bool) Cars {
	cars := make([]*Car, 0)
	c.Process(func(car *Car) {
		if f(car) {
			cars = append(cars, car)
		}
	})
	return cars
}

func (c Cars) Map(f func(car *Car) Any) []Any {
	result := make([]Any, len(c))
	ix := 0
	c.Process(func(car *Car) {
		result[ix] = car
		ix++
	})
	return result
}

func MakeSortedAppender(manufacturers []string) (func(car *Car), map[string]Cars) {
	sortedCars := make(map[string]Cars)
	for _, m := range manufacturers {
		sortedCars[m] = make([]*Car, 0)
	}
	sortedCars["Default"] = make([]*Car, 0)
	f := func(c *Car) {
		if _, ok := sortedCars[c.Manufacturer]; ok {
			sortedCars[c.Manufacturer] = append(sortedCars[c.Manufacturer], c)
		} else {
			sortedCars["Default"] = append(sortedCars["Default"], c)
		}
	}
	return f, sortedCars
}

func main() {
	// make some cars:
	allCars := Cars([]*Car{
		&Car{"Fiesta", "Ford", 2008},
		&Car{"XL 450", "BMW", 2011},
		&Car{"XL 450", "BMW", 2011},
		&Car{"D600", "Mercedes", 2009},
		&Car{"X 800", "BMW", 2008},
	})

	allNewBMWs := allCars.FindAll(func(car *Car) bool {
		return (car.Manufacturer == "BMW") && (car.BuildYear > 2010)
	})

	fmt.Println("AllCars: ", allCars)
	fmt.Println("New BMWs: ", allNewBMWs)

	manufacturers := []string{"Ford", "Aston Martin", "Land Rover", "BMW", "Jaguar"}
	sortedAppender, sortedCars := MakeSortedAppender(manufacturers)
	allCars.Process(sortedAppender)
	fmt.Println("Map sortedCars: ", sortedCars)
	BMWCount := len(sortedCars["BMW"])
	fmt.Println("We have ", BMWCount, " BMWs")
}
